﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using BattleCity.GameLogic;

namespace BattleCity.Rendering
{
    public enum TankTypes
    {
        Player,
        Silver,
        Black,
        Purple,
        RedTransition,
        GreenTransition,
        Stealth
    }

    public struct TankInfos
    {
        public TankTypes tankType;
        public Matrix world;
        public float wheelRollTheta;
        public bool visible;
    }
  
    class TanksRenderer
    {
        const short PLAYER  = 0;
        const short SILVER  = 1;
        const short BLACK   = 2;
        const short BLUE  = 3;
        const short REDTR   = 4;
        const short GREENTR = 5;
        const short STEALTH = 6;

        const short BASE   = 0;
        const short TURRET = 1;

        const short LEFTFRONT = 0;
        const short LEFTBACK = 1;
        const short RIGHTFRONT = 2;
        const short RIGHTBACK = 3;

        const short UP    = 0;
        const short DOWN  = 1;
        const short LEFT  = 2;
        const short RIGHT = 3;

        Matrix[] rotations;

        Model[] tankModels;
        Texture2D[,] tankTextures;
        Texture2D[] currentTankTextures;

        Matrix[] wheelTransform;
        Matrix[] wheelTransformInverse;

        TankInfos[] tankInfos;
        int tanksCount;
        float tanksScale;

        Matrix wheelRollMatrix;

        public TanksRenderer(ContentManager content, Matrix projection)
        {
            tankModels = new Model[4];
            tankModels[0] = content.Load<Model>(@"tank_models\tank_1");
            tankModels[1] = content.Load<Model>(@"tank_models\tank_2");
            tankModels[2] = content.Load<Model>(@"tank_models\tank_3");
            tankModels[3] = content.Load<Model>(@"tank_models\tank_4");

            tankTextures = new Texture2D[7, 2];
            tankTextures[PLAYER, BASE] = content.Load<Texture2D>(@"tank_textures\player_tank\engine_diff_tex");
            tankTextures[PLAYER, TURRET] = content.Load<Texture2D>(@"tank_textures\player_tank\turret_alt_diff_tex");
            tankTextures[SILVER, BASE] = content.Load<Texture2D>(@"tank_textures\silver_tank\engine_diff_tex");
            tankTextures[SILVER, TURRET] = content.Load<Texture2D>(@"tank_textures\silver_tank\turret_alt_diff_tex");
            tankTextures[BLACK, BASE] = content.Load<Texture2D>(@"tank_textures\black_tank\engine_diff_tex");
            tankTextures[BLACK, TURRET] = content.Load<Texture2D>(@"tank_textures\black_tank\turret_alt_diff_tex");
            tankTextures[BLUE, BASE] = content.Load<Texture2D>(@"tank_textures\blue_tank\engine_diff_tex");
            tankTextures[BLUE, TURRET] = content.Load<Texture2D>(@"tank_textures\blue_tank\turret_alt_diff_tex");
            tankTextures[REDTR, BASE] = content.Load<Texture2D>(@"tank_textures\red_transition\transition");
            tankTextures[REDTR, TURRET] = content.Load<Texture2D>(@"tank_textures\red_transition\transition");
            tankTextures[GREENTR, BASE] = content.Load<Texture2D>(@"tank_textures\green_transition\transition");
            tankTextures[GREENTR, TURRET] = content.Load<Texture2D>(@"tank_textures\green_transition\transition");
            tankTextures[STEALTH, BASE] = content.Load<Texture2D>(@"tank_textures\stealth_tank\stealth");
            tankTextures[STEALTH, TURRET] = content.Load<Texture2D>(@"tank_textures\stealth_tank\stealth");

            wheelTransform = new Matrix[4];
            wheelTransform[LEFTFRONT] = Matrix.CreateTranslation(-tankModels[0].Meshes[0].BoundingSphere.Center);
            wheelTransform[RIGHTBACK] = Matrix.CreateTranslation(-tankModels[0].Meshes[2].BoundingSphere.Center);
            wheelTransform[RIGHTFRONT] = Matrix.CreateTranslation(-tankModels[0].Meshes[3].BoundingSphere.Center);
            wheelTransform[LEFTBACK] = Matrix.CreateTranslation(-tankModels[0].Meshes[10].BoundingSphere.Center);

            wheelTransformInverse = new Matrix[4];
            wheelTransformInverse[LEFTFRONT] = Matrix.CreateTranslation(tankModels[0].Meshes[0].BoundingSphere.Center);
            wheelTransformInverse[RIGHTBACK] = Matrix.CreateTranslation(tankModels[0].Meshes[2].BoundingSphere.Center);
            wheelTransformInverse[RIGHTFRONT] = Matrix.CreateTranslation(tankModels[0].Meshes[3].BoundingSphere.Center);
            wheelTransformInverse[LEFTBACK] = Matrix.CreateTranslation(tankModels[0].Meshes[10].BoundingSphere.Center);

            foreach (Model tankModel in tankModels)
            {
                foreach(ModelMesh mesh in tankModel.Meshes)
                {
                    foreach (BasicEffect fx in mesh.Effects)
                    {
                        fx.Projection = projection;
                        if (Options.tanksLights) fx.LightingEnabled = true;
                        else fx.LightingEnabled = false;
                    }
                }
            }

            tankInfos = new TankInfos[4];

            for (int i = 0; i < 4; ++i)
            {
                tankInfos[i] = new TankInfos();
                tankInfos[i].world = new Matrix();
                tankInfos[i].visible = true;
            }

            rotations = new Matrix[4];
            rotations[UP] = Matrix.CreateRotationY(0f);
            rotations[RIGHT] = Matrix.CreateRotationY(MathHelper.PiOver2); 
            rotations[LEFT] = Matrix.CreateRotationY(-MathHelper.PiOver2);
            rotations[DOWN] = Matrix.CreateRotationY(MathHelper.Pi);

            currentTankTextures = new Texture2D[2];
        }

        public void Update(Matrix view)
        {
            tanksCount = 1;

           
            tankInfos[1].visible = true;
            tankInfos[2].visible = true;
            tankInfos[3].visible = true;
            
          
            //Determino lo stato del carro giocatore
            if (GameState.gameState.PlayerState.Value.IsDestroyed)
            {
                tankInfos[0].tankType = TankTypes.RedTransition;
                tanksScale = (float)GameState.gameState.PlayerTransitionClock.Value;
            }
            else if (GameState.gameState.PlayerState.Value.IsSpawning)
            {
                tankInfos[0].tankType = TankTypes.GreenTransition;
                tanksScale = (float)GameState.gameState.PlayerTransitionClock.Value;
            }
            else
            {
                if(GameState.shieldEnabled.Value)
                {
                    tankInfos[0].tankType = TankTypes.GreenTransition;
                }
                else if (GameState.gameState.PlayerTank.Value.IsStealth)
                {
                    
                    tankInfos[0].tankType = TankTypes.Stealth;
                }
                else
                {
                    tankInfos[0].tankType = TankTypes.Player;
                }
                tanksScale = 1.0f;
            }

            if (GameState.gameState.PlayerTank.Value.Direction.X == 1)          tankInfos[0].world = Matrix.CreateScale(tanksScale) * rotations[LEFT];
            else if (GameState.gameState.PlayerTank.Value.Direction.X == -1)    tankInfos[0].world = Matrix.CreateScale(tanksScale) * rotations[RIGHT];
            else if (GameState.gameState.PlayerTank.Value.Direction.Y == -1)    tankInfos[0].world = Matrix.CreateScale(tanksScale) * rotations[UP];
            else                                                                tankInfos[0].world = Matrix.CreateScale(tanksScale) * rotations[DOWN];

            tankInfos[0].wheelRollTheta = (float)GameState.gameState.PlayerTank.Value.WheelRollTheta;
            tankInfos[0].world.Translation = GameState.gameState.PlayerTank.Value.Position.xnaVector3YView;
            

            //Determino lo stato dei carri nemici
            foreach (Casanova.Variable<Types.Tank> tank in GameState.gameState.EnemiesList.Value)
            {
                if (tank.Value.IsStealth) tankInfos[tanksCount].tankType = TankTypes.Stealth;
                else
                {
                    Types.EnemyType EnemyType = Types.getEnemyType(tank.Value.TankType);
                    if (EnemyType.IsBlack) tankInfos[tanksCount].tankType = TankTypes.Black;
                    else if (EnemyType.IsBlue) tankInfos[tanksCount].tankType = TankTypes.Purple;
                    else if (EnemyType.IsSilver) tankInfos[tanksCount].tankType = TankTypes.Silver;
                }

                if (tank.Value.Direction.X == 1) tankInfos[tanksCount].world = rotations[LEFT];
                else if (tank.Value.Direction.X == -1) tankInfos[tanksCount].world = rotations[RIGHT];
                else if (tank.Value.Direction.Y == -1) tankInfos[tanksCount].world = rotations[UP];
                else tankInfos[tanksCount].world = rotations[DOWN];

                if ((Math.Abs(tank.Value.Position.X - GameState.gameState.PlayerTank.Value.Position.X) > 30)
                || (Math.Abs(tank.Value.Position.Y - GameState.gameState.PlayerTank.Value.Position.Y) > 24))
                        tankInfos[tanksCount].visible = false;

                tankInfos[tanksCount].world.Translation = tank.Value.Position.xnaVector3YView;

                tankInfos[tanksCount].wheelRollTheta = (float)tank.Value.WheelRollTheta;

                ++tanksCount;
            }

            foreach (Tuple<Types.Tank, Double> tank in GameState.gameState.DestroyedEnemiesList.Value)
            {
                tanksScale = (float)tank.Item2;

                if (tank.Item1.Direction.X == 1) tankInfos[tanksCount].world = Matrix.CreateScale(tanksScale) * rotations[LEFT];
                else if (tank.Item1.Direction.X == -1) tankInfos[tanksCount].world = Matrix.CreateScale(tanksScale) * rotations[RIGHT];
                else if (tank.Item1.Direction.Y == -1) tankInfos[tanksCount].world = Matrix.CreateScale(tanksScale) * rotations[UP];
                else tankInfos[tanksCount].world = Matrix.CreateScale(tanksScale) * rotations[DOWN];

                if ((Math.Abs(tank.Item1.Position.X - GameState.gameState.PlayerTank.Value.Position.X) > 30)
                || (Math.Abs(tank.Item1.Position.Y - GameState.gameState.PlayerTank.Value.Position.Y) > 24))
                        tankInfos[tanksCount].visible = false;

                tankInfos[tanksCount].tankType = TankTypes.GreenTransition;

                tankInfos[tanksCount].world.Translation = tank.Item1.Position.xnaVector3YView;

                ++tanksCount;
            }

            foreach (Tuple<Types.Tank, Double> tank in GameState.gameState.SpawnedEnemiesList.Value)
            {
                tanksScale = (float)tank.Item2;

                if ((Math.Abs(tank.Item1.Position.X - GameState.gameState.PlayerTank.Value.Position.X) > 30)
                || (Math.Abs(tank.Item1.Position.Y - GameState.gameState.PlayerTank.Value.Position.Y) > 24))
                        tankInfos[tanksCount].visible = false;

                tankInfos[tanksCount].tankType = TankTypes.RedTransition;
                if (tank.Item1.Direction.X == 1) tankInfos[tanksCount].world = Matrix.CreateScale(tanksScale) * rotations[LEFT];
                else if (tank.Item1.Direction.X == -1) tankInfos[tanksCount].world = Matrix.CreateScale(tanksScale) * rotations[RIGHT];
                else if (tank.Item1.Direction.Y == -1) tankInfos[tanksCount].world = Matrix.CreateScale(tanksScale) * rotations[UP];
                else tankInfos[tanksCount].world = Matrix.CreateScale(tanksScale) * rotations[DOWN];
                tankInfos[tanksCount].world.Translation = tank.Item1.Position.xnaVector3YView;

                ++tanksCount;
            }       


            for (int t = 0; t < tanksCount; ++t)
            {
                switch (tankInfos[t].tankType)
                {
                    case TankTypes.Player:
                        currentTankTextures[BASE] = tankTextures[PLAYER, BASE];
                        currentTankTextures[TURRET] = tankTextures[PLAYER, TURRET];
                        break;

                    case TankTypes.Silver:
                        currentTankTextures[BASE] = tankTextures[SILVER, BASE];
                        currentTankTextures[TURRET] = tankTextures[SILVER, TURRET];
                        break;

                    case TankTypes.Purple:
                        currentTankTextures[BASE] = tankTextures[BLUE, BASE];
                        currentTankTextures[TURRET] = tankTextures[BLUE, TURRET];
                        break;

                    case TankTypes.Black:
                        currentTankTextures[BASE] = tankTextures[BLACK, BASE];
                        currentTankTextures[TURRET] = tankTextures[BLACK, TURRET];
                        break;

                    case TankTypes.GreenTransition:
                        currentTankTextures[BASE] = tankTextures[GREENTR, BASE];
                        currentTankTextures[TURRET] = tankTextures[GREENTR, TURRET];
                        break;

                    case TankTypes.RedTransition:
                        currentTankTextures[BASE] = tankTextures[REDTR, BASE];
                        currentTankTextures[TURRET] = tankTextures[REDTR, TURRET];
                        break;

                    case TankTypes.Stealth:
                        currentTankTextures[BASE] = tankTextures[STEALTH, BASE];
                        currentTankTextures[TURRET] = tankTextures[STEALTH, TURRET];
                        break;
                }

                for (int i = 0; i < tankModels[t].Meshes.Count; ++i)
                {
                    foreach (BasicEffect fx in tankModels[t].Meshes[i].Effects)
                    {
                        fx.View = view;
    
                        switch (i)
                        {
                            // le mesh di indice 4,6,8,9,12 sono relative alla torretta, le altre alle ruote e base del carro
                            case 4:
                            case 6:
                            case 8:
                            case 9:
                            case 12:
                                fx.Texture = currentTankTextures[TURRET];
                                break;
                            default:
                                fx.Texture = currentTankTextures[BASE];
                                break;
                        }
                    }
                }
            }
        }

        public void Draw(bool topView)
        {
            for (int t = 0; t < tanksCount; ++t)
            {
#if WINDOWS_PHONE
                if(tankInfos[t].visible || topView)
#endif
                {
                    int i = 0;
                    wheelRollMatrix = Matrix.CreateRotationX(-tankInfos[t].wheelRollTheta);

                    foreach (ModelMesh mesh in tankModels[t].Meshes)
                    {
                        foreach (BasicEffect fx in tankModels[t].Meshes[i].Effects)
                        {
                            switch (i)
                            {
                                case 0:
                                    fx.World = wheelTransform[LEFTFRONT] * wheelRollMatrix * wheelTransformInverse[LEFTFRONT] * tankInfos[t].world;
                                    break;
                                case 2:
                                    fx.World = wheelTransform[RIGHTBACK] * wheelRollMatrix * wheelTransformInverse[RIGHTBACK] * tankInfos[t].world;
                                    break;
                                case 3:
                                    fx.World = wheelTransform[RIGHTFRONT] * wheelRollMatrix * wheelTransformInverse[RIGHTFRONT] * tankInfos[t].world;
                                    break;
                                case 10:
                                    fx.World = wheelTransform[LEFTBACK] * wheelRollMatrix * wheelTransformInverse[LEFTBACK] * tankInfos[t].world;
                                    break;

                                default:
                                    fx.World = tankInfos[t].world;
                                    break;
                            }
                        }

                        ++i;
                        mesh.Draw();
                    }
                }
            }
        }
     
    }
}
